#include "FileUpLoadUpdate.h"
#include "Sigin.h"
#include "CryptoUtil.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>     
#include <nlohmann/json.hpp>
#include <workflow/MySQLResult.h>
#include <wfrest/PathUtil.h>
#include <map>
#include <string>
#include <sstream>
#include <iostream>
#include <functional>
using std::map;
using std::string;
using std::function;
using std::ostringstream;
using std::bind;
using namespace protocol;
using namespace wfrest;

using JSON = nlohmann::json;


FileUpLoad::FileUpLoad(wfrest::HttpServer & server)
:m_server(server)
{}

struct FileUpLoadContext{
    size_t size;
    HttpResp * resp;
    string sha256;
    string filename;
    string path;
    string content;
};

void fileuploadMysqlCallback2(WFMySQLTask * mysqlTask){
    FileUpLoadContext * context = static_cast<FileUpLoadContext*>(series_of(mysqlTask)->get_context());
    if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
        context->resp->String("mysqlTask failed");
        return;
    }

    MySQLResultCursor cursor(mysqlTask->get_resp());

    if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
        context->resp->String("error77");
        return ;
    }

    if(cursor.get_cursor_status()==MYSQL_STATUS_OK){
        context->resp->Redirect("/static/view/home.html", HttpStatusSeeOther);
    }else{
        context->resp->String("error44");           
    }

}

void fileuploadMysqlCallback1(WFMySQLTask * mysqlTask){
    FileUpLoadContext * context = static_cast<FileUpLoadContext*>(series_of(mysqlTask)->get_context());
    if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
        context->resp->String("mysqlTask failed");
        return;
    }

    MySQLResultCursor cursor(mysqlTask->get_resp());

    if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
        context->resp->String("error11");
        return ;
    }

    if(cursor.get_cursor_status()==MYSQL_STATUS_GET_RESULT){
        map<string,MySQLCell> row;
        cursor.fetch_row(row);
        if(row.size()==0){
            context->resp->String("error22");
            return;
        }
        int id = row["id"].as_int();
        ostringstream oss ;
        oss<<"insert into tbl_user_file (uid,filename,hashcode,size) values ('"<<id<<"','"<<context->filename<<"','"<<context->sha256<<"','"<<context->size<<"');";
        string sql = oss.str();
        WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,fileuploadMysqlCallback2);
        mysqltask->get_req()->set_query(sql);
        series_of(mysqlTask)->set_context(context);
        series_of(mysqlTask)->push_back(mysqltask);
    
    }else{
            context->resp->String("error33");           
    }

}

void FileUpLoad::fileupload_module(){
    m_server.POST("/file/upload",[](const HttpReq* req, HttpResp* resp,SeriesWork* series){
        string username;
        string token;
        username = req->query("username");
        token = req->query("token");

        if(CryptoUtil::verify_token(token,username)){
            if (req->content_type() != MULTIPART_FORM_DATA) {
                resp->set_status(HttpStatusBadRequest);     
                return ;
            }

            string dirPath = "./file/"+username; 
            DIR * ret = ::opendir(dirPath.c_str());
            if(ret == NULL){
                ::mkdir(dirPath.c_str(),0777);
            }

            
            const Form& form = req->form();

            for (const auto& [_, file]: form) {
        
                const auto& [filename, content] = file;
                string path  = "./file/"+username+"/"+PathUtil::base(filename);
                int fd = ::open(path.c_str(),O_RDONLY);
                size_t size = ::lseek(fd, 0, SEEK_END);
                string sha256 = CryptoUtil::hash_file(fd);

                #ifdef DEBUG
                    std::cout<<sha256<<"\n";
                #endif

                FileUpLoadContext * context = new FileUpLoadContext{size,resp,sha256,filename,path,content};

                string sql = "select * from tbl_users where username = '"+username+"';";
                WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,fileuploadMysqlCallback1);
                mysqltask->get_req()->set_query(sql);
                series->set_context(context);
                series->push_back(mysqltask);
                series->set_callback([context](const SeriesWork*){
                    delete context;
                });
                
               // resp->Save(path, std::move(content),[resp,username,filename,path,series](const FileIOArgs *){
               // });
            }
            
        }else{

            resp->String("error");
        
        }
    });    
}

void FileUpLoad::start(){
    fileupload_module();
}